#region Copyright Vanderlande Industries B.V. 2009-2011
//
// Copyright (c) 2009-2011 Vanderlande Industries.
// All rights reserved.
//
// @file:     SubscriptionManager.cs
// @version:  %version: 6 %
// %date_created: woensdag 16 november 2011 15:51:10 %
//
// The copyright to the computer program(s) herein is the property of
// Vanderlande Industries. The program(s) may be used and/or copied
// only with the written permission of the owner or in accordance with
// the terms and conditions stipulated in the contract under which the
// program(s) have been supplied.
//
#endregion

using System.Collections;
using System.Reflection;
using System.Threading;
//using VI.Bfw.Logging;
using VI.Fsc.OperatorGui.WebReferences.Opc;

namespace VI.Fsc.OperatorGui.Models.Subscription
{
    /// <summary>
    /// Internal class holding an instance of a Subscription object and an instance of 
    /// a Thread in which the subscription is being executed.
    /// </summary>
    internal class CSubscriptionItem
    {
        public CSubscription Subscription;
        public Thread Thread;

        public CSubscriptionItem(CSubscription subscription, Thread thread)
        {
            this.Subscription = subscription;
            this.Thread = thread;
        }
    }

    /// The CSubscriptionManager follows the 'Singleton' pattern. This manager
    /// takes care of all the subscriptions: starts/stops them, manages a thread
    /// for each of them, aborts the threads when needed, handle errors, etc. This
    /// is an itnerface between the subscription itself and the class that needs a subscription.
    /// </summary>
    public class CSubscriptionManager
    {
        private static CSubscriptionManager instance;
        private static object syncLock = new object();

        private Hashtable subscriptionItemsHash;
  
        private CSubscriptionManager()
        {
            this.subscriptionItemsHash = new Hashtable();
        }

        /// <summary>
        /// Get the singleton register instance.
        /// </summary>
        /// <returns>Reference to a single register class.</returns>
        public static CSubscriptionManager Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncLock)
                    {
                        if (instance == null)
                        {
                            instance = new CSubscriptionManager();
                        }
                    }
                }
                return instance;
            }
        }



        public string StartNewSubscription(OpcXmlDaWebService server, short pingRate,
            SubscribeRequestItemList list, SubscribedItemsChangedEventHandler changeEventHandler,
            SubscriptionExceptionEventHandler exceptionEventHandler)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod());

            // Creates a new subscription based on the parameters.
            CSubscription subscription = new CSubscription(server, pingRate, list);
            subscription.SubscribedItemsChanged += changeEventHandler;
            subscription.ExceptionEvent += exceptionEventHandler;

            // Creates a new thread that will run the Start of the subscription.
            Thread thread = new Thread(subscription.Start);
            thread.IsBackground = true;

            // Creates a new SubscriptionItem object that holds both the subscription reference
            // and the thread reference.
            CSubscriptionItem subscriptionItem = new CSubscriptionItem(subscription, thread);

            // Save a reference to the item in the hash table.
            lock (subscriptionItemsHash)
            {
                subscriptionItemsHash.Add(subscription.Id, subscriptionItem);
            }

            // Starts the subscription in a new thread.
            thread.Start();

            return subscription.Id;
        }

        /// <summary>
        /// Stop the subscription of an item.
        /// </summary>
        /// <remarks>
        /// <list type="ordered">
        /// <item>First stop the subscription polling.</item>  
        /// <item>If there is a thread associated to this subscription, abort it.</item>  
        /// <item>The subscriptionItem is useless, remove it from the hash.</item>  
        /// </remarks>
        /// <param name="subscriptionId">The ID of the item for which subscription is to be stopped.</param>
        /// <param name="notifyWhenStopped">
        /// true: An event notifying about this change of the subscription is raised.</br>
        /// false: No event is raised.</param>
        public void StopSubscription(string subscriptionId, bool notifyWhenStopped)
        {
            //Logger.Entering(MethodBase.GetCurrentMethod(), "subscriptionId = " + subscriptionId);

            if (subscriptionItemsHash.ContainsKey(subscriptionId))
            {
                // 1. First stop the subscription polling.
                CSubscriptionItem subscriptionItem =
                    (CSubscriptionItem)this.subscriptionItemsHash[subscriptionId];
                subscriptionItem.Subscription.Stop(notifyWhenStopped);

                // 2. If there is a thread associated to this subscription, stop it.
                if ((null != subscriptionItem.Thread) && subscriptionItem.Thread.IsAlive)
                {
                    int waitMilliseconds = 200;
                    //Logger.Debug(MethodBase.GetCurrentMethod(), "Trying to stop thread gracefully.");
                    bool stopped = subscriptionItem.Thread.Join(waitMilliseconds);
                    if (!stopped)
                    {
                        //Logger.Debug(MethodBase.GetCurrentMethod(), "Thread was not stopped - abort.");
                        try
                        {
                            subscriptionItem.Thread.Abort();
                        }
                        catch (ThreadAbortException ex)
                        {
                            //Logger.Info(MethodBase.GetCurrentMethod(), "Needed to abort thread: " + ex.Message);
                        }
                    }
                }

                // 3. The subscriptionItem is useless, remove it from the hash.
                this.subscriptionItemsHash.Remove(subscriptionId);
            }

            //Logger.Exiting(MethodBase.GetCurrentMethod());
        }
    }
}
